// SPDX-License-Identifier: GPL-2.0
/*
 * base from kernel 4.x.
 * Description: stmmac legacy rx and phy driver mode
 */

static inline void imx_i2c_write_reg(unsigned int val,
		struct imx_i2c_struct *i2c_imx, unsigned int reg);

static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx,
		unsigned int reg);

enum pinmux_endian_type {
	BIG_ENDIAN,
	LITTLE_ENDIAN,
};

struct pinmux_cfg {
	enum pinmux_endian_type endian; /* endian of RCWPMUXCR0 */
	u32 pmuxcr_offset;
	u32 pmuxcr_set_bit;		    /* pin mux of RCWPMUXCR0 */
};

static struct pinmux_cfg ls1012a_pinmux_cfg = {
	.endian = BIG_ENDIAN,
	.pmuxcr_offset = 0x430,
	.pmuxcr_set_bit = 0x10,
};

static struct pinmux_cfg ls1043a_pinmux_cfg = {
	.endian = BIG_ENDIAN,
	.pmuxcr_offset = 0x40C,
	.pmuxcr_set_bit = 0x10,
};

static struct pinmux_cfg ls1046a_pinmux_cfg = {
	.endian = BIG_ENDIAN,
	.pmuxcr_offset = 0x40C,
	.pmuxcr_set_bit = 0x80000000,
};

static const struct of_device_id pinmux_of_match[] = {
	{ .compatible = "fsl,ls1012a-vf610-i2c", .data = &ls1012a_pinmux_cfg},
	{ .compatible = "fsl,ls1043a-vf610-i2c", .data = &ls1043a_pinmux_cfg},
	{ .compatible = "fsl,ls1046a-vf610-i2c", .data = &ls1046a_pinmux_cfg},
	{},
};
MODULE_DEVICE_TABLE(of, pinmux_of_match);

/* The SCFG, Supplemental Configuration Unit, provides SoC specific
 * configuration and status registers for the device. There is a
 * SDHC IO VSEL control register on SCFG for some platforms. It's
 * used to support SDHC IO voltage switching.
 */
static const struct of_device_id scfg_device_ids[] = {
	{ .compatible = "fsl,ls1012a-scfg", },
	{ .compatible = "fsl,ls1043a-scfg", },
	{ .compatible = "fsl,ls1046a-scfg", },
	{}
};

/*
 * Based on the I2C specification, if the data line (SDA) is
 * stuck low, the master should send nine  * clock pulses.
 * The I2C slave device that held the bus low should release it
 * sometime within  * those nine clocks. Due to this erratum,
 * the I2C controller cannot generate nine clock pulses.
 */
static int i2c_imx_recovery_for_layerscape(struct imx_i2c_struct *i2c_imx)
{
	u32 pmuxcr = 0;
	int ret;
	unsigned int i, temp;

	/* configure IICx_SCL/GPIO pin as a GPIO */
	if (i2c_imx->need_set_pmuxcr == 1) {
		pmuxcr = ioread32be(i2c_imx->pmuxcr_addr);
		if (i2c_imx->pmuxcr_endian == BIG_ENDIAN)
			iowrite32be(i2c_imx->pmuxcr_set|pmuxcr,
					i2c_imx->pmuxcr_addr);
		else
			iowrite32(i2c_imx->pmuxcr_set|pmuxcr,
					i2c_imx->pmuxcr_addr);
	}

	ret = gpio_request(i2c_imx->gpio, i2c_imx->adapter.name);
	if (ret) {
		dev_err(&i2c_imx->adapter.dev,
			"can't get gpio: %d\n", ret);
		return ret;
	}

	/* Configure GPIO pin as an output and open drain. */
	gpio_direction_output(i2c_imx->gpio, 1);
	udelay(10);

	/* Write 9 pulses to recovery I2C, include cpld i2c */
	for (i = 0; i < 10; i++) {
		gpio_set_value(i2c_imx->gpio, 1);
		udelay(10);
		gpio_set_value(i2c_imx->gpio, 0);
		udelay(10);
	}
	/* ensure that the last level sent is always high */
	gpio_direction_input(i2c_imx->gpio);

	gpio_free(i2c_imx->gpio);
	/*
	 * Set I2Cx_IBCR = 0h00 to generate a STOP and then
	 * set I2Cx_IBCR = 0h80 to reset
	 */
	temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
	temp &= ~(I2CR_MSTA | I2CR_MTX);
	imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);

	/* Restore the saved value of the register SCFG_RCWPMUXCR0 */
	if (i2c_imx->need_set_pmuxcr == 1) {
		if (i2c_imx->pmuxcr_endian == BIG_ENDIAN)
			iowrite32be(pmuxcr, i2c_imx->pmuxcr_addr);
		else
			iowrite32(pmuxcr, i2c_imx->pmuxcr_addr);
	}
	/*
	 * Set I2C_IBSR[IBAL] to clear the IBAL bit if-
	 * I2C_IBSR[IBAL] = 1
	 */
	temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
	if (temp & I2SR_IAL) {
		temp &= ~I2SR_IAL;
		dev_err(&i2c_imx->adapter.dev, "recovery i2c layerscape\n");
		imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
	}
	return 0;
}

/*
 * switch SCL and SDA to their GPIO function and do some bitbanging
 * for bus recovery.
 * There are platforms such as Layerscape that don't support pinctrl, so add
 * workaround for layerscape, it has no effect for other platforms.
 */
static int i2c_imx_init_recovery_for_layerscape(
		struct imx_i2c_struct *i2c_imx,
		struct platform_device *pdev)
{
	const struct of_device_id *of_id;
	struct device_node *np		= pdev->dev.of_node;
	struct pinmux_cfg		*pinmux_cfg;
	struct device_node *scfg_node;
	void __iomem *scfg_base = NULL;

	i2c_imx->gpio = of_get_named_gpio(np, "fsl-scl-gpio", 0);
	if (!gpio_is_valid(i2c_imx->gpio)) {
		dev_info(&pdev->dev, "fsl-scl-gpio not found\n");
		return 0;
	}
	pinmux_cfg = devm_kzalloc(&pdev->dev, sizeof(*pinmux_cfg), GFP_KERNEL);
	if (!pinmux_cfg)
		return -ENOMEM;

	i2c_imx->need_set_pmuxcr = 0;
	of_id = of_match_node(pinmux_of_match, np);
	if (of_id) {
		pinmux_cfg = (struct pinmux_cfg *)of_id->data;
		i2c_imx->pmuxcr_endian = pinmux_cfg->endian;
		i2c_imx->pmuxcr_set = pinmux_cfg->pmuxcr_set_bit;
		scfg_node = of_find_matching_node(NULL, scfg_device_ids);
		if (scfg_node) {
			scfg_base = of_iomap(scfg_node, 0);
			if (scfg_base) {
				i2c_imx->pmuxcr_addr = scfg_base + pinmux_cfg->pmuxcr_offset;
				i2c_imx->need_set_pmuxcr = 1;
			}
		}
	}
	i2c_imx->layerscape_bus_recover = 1;
	return 0;
}
